package com.wispx.gateway.config.manager;

import com.wispx.gateway.config.pojo.RouteDefinition;
import com.wispx.gateway.config.pojo.ServiceDefinition;
import com.wispx.gateway.config.pojo.ServiceInstance;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 动态配置管理，缓存从配置中心拉取下来的配置
 */
public class DynamicConfigManager {

    private static final DynamicConfigManager INSTANCE = new DynamicConfigManager();

    // 监听路由变化Map
    private final Map<String/*服务名*/, List<RouteListener>> routeListenserMap = new ConcurrentHashMap<>();

    // id serviceName uri -> route
    private final Map<String/*路由id*/, RouteDefinition> routeId2routeMap = new ConcurrentHashMap<>();
    private final Map<String/*服务名*/, RouteDefinition> serviceName2routeMap = new ConcurrentHashMap<>();
    private final Map<String/*uri路径*/, RouteDefinition> uri2routeMap = new ConcurrentHashMap<>();

    // 服务、服务实例(服务名 + 服务id 确定 实例)
    private final Map<String/*服务名*/, ServiceDefinition> services = new ConcurrentHashMap<>();
    private final Map<String/*服务名*/, Map<String/*服务id*/, ServiceInstance>> serviceInstances = new ConcurrentHashMap<>();

    // 单例
    private DynamicConfigManager() {

    }

    public static DynamicConfigManager getInstance() {
        return INSTANCE;
    }

    // 监听
    public boolean addRouteListener(String serviceName, RouteListener routeListener) { // 仅仅添加到Map中，并不会回调，需要手动回调
        return routeListenserMap.computeIfAbsent(serviceName, item -> new CopyOnWriteArrayList<>()).add(routeListener);
    }

    public void changeRoute(RouteDefinition route) {
        if (routeListenserMap == null || routeListenserMap.isEmpty()) {
            return;
        }
        List<RouteListener> routeListeners = routeListenserMap.get(route.getServiceName());
        if (routeListeners == null || routeListeners.isEmpty()) {
            return;
        }
        for (RouteListener routeListener : routeListeners) {
            if (routeListener == null) {
                continue;
            }
            routeListener.routeOnChange(route);
        }
    }

    // 路由
    public void updateRouteByRouteId(String routeId, RouteDefinition route) {
        routeId2routeMap.put(routeId, route);
    }

    public void updateRoutes(Collection<RouteDefinition> routes, boolean isClear) {
        if (routes == null || routes.isEmpty()) {
            return;
        }
        if (isClear) {
            routeId2routeMap.clear();
            serviceName2routeMap.clear();
            uri2routeMap.clear();
        }
        for (RouteDefinition route : routes) {
            if (route == null) {
                continue;
            }
            routeId2routeMap.put(route.getId(), route);
            serviceName2routeMap.put(route.getServiceName(), route);
            uri2routeMap.put(route.getUri(), route);
        }
    }

    public void updateRoutes(Collection<RouteDefinition> routes) {
        updateRoutes(routes, false);
    }

    public RouteDefinition getRouteByRouteId(String routeId) {
        return routeId2routeMap.get(routeId);
    }

    public RouteDefinition getRouteByServiceName(String serviceName) {
        return serviceName2routeMap.get(serviceName);
    }

    public RouteDefinition getRouteByUri(String uri) {
        return uri2routeMap.get(uri);
    }

    public Set<Map.Entry<String, RouteDefinition>> allUriRouteEntrySet() {
        return uri2routeMap.entrySet();
    }

    // 服务
    public void updateService(ServiceDefinition service) {
        services.put(service.getServiceName(), service);
    }

    public ServiceDefinition getServiceByServiceName(String serviceName) {
        return services.get(serviceName);
    }

    // 实例
    public void addServiceInstance(String serviceName, ServiceInstance instance) {
        serviceInstances.computeIfAbsent(serviceName, item -> new ConcurrentHashMap<>()).put(serviceName, instance);
    }

    public void updateServiceInstance(ServiceDefinition service, Set<ServiceInstance> instances) {
        Map<String, ServiceInstance> oldInstances = serviceInstances
                .computeIfAbsent(service.getServiceName(), item -> new ConcurrentHashMap<>());
        oldInstances.clear();
        for (ServiceInstance newInstance : instances) {
            oldInstances.put(newInstance.getInstanceId(), newInstance);
        }
    }

    public void removeServiceInstance(String serviceName, ServiceInstance instance) {
        serviceInstances.compute(serviceName, (key, instances) -> {
            if (instances == null || instances.get(instance.getInstanceId()) == null) {
                return null;
            }
            instances.remove(instance.getInstanceId());
            return instances;
        });
    }

    public Map<String, ServiceInstance> getServiceInstanceByServiceName(String serviceName) {
        return serviceInstances.get(serviceName);
    }

}
